package edu.moravian.entity;

import edu.moravian.CollisionPhysics.EntityCollisionEngine;
import edu.moravian.math.Point2D;
import edu.moravian.math.Rand;
import edu.moravian.math.Vector2D;
import edu.moravian.world.WorldDescriptor;
import java.util.ArrayList;
import java.util.List;

/**
 * The manager for the ball entities in the bouncing ball game
 *
 * @author ronaghan
 */
public class BallEntityManager
{
    private WorldDescriptor world;
    private EntityCollisionEngine collisionEngine;
    private List<BallEntity> balls;
    
    private List<BallPair> collisionPairs;

    /**
     * Create all the balls in the world based on properties 
     * specified by the ball properties and place the inside the world
     * 
     * @param world information about the world
     */
    public BallEntityManager(WorldDescriptor world)
    {
        this.world = world;

        BallProperties ballProps = new BallProperties();

        collisionPairs = new ArrayList<BallPair>();
        collisionEngine = new EntityCollisionEngine(ballProps.getElasticity());

        //populate our list of balls from values in the properties file
        balls = new ArrayList<BallEntity>();
        for (int i = 0; i < ballProps.getNumBalls(); i++)
        {
            double speed = Rand.inRange(ballProps.getMinSpeed(), ballProps.getMaxSpeed());
            double radius = Rand.inRange(ballProps.getMinRadius(), ballProps.getMaxRadius());

            double minX = world.getMinX() + radius;
            double maxX = world.getMaxX() - radius;
            double minY = world.getMinY() + radius;
            double maxY = world.getMaxY() - radius;

            Point2D loc = Point2D.randomPoint(minX, maxX, minY, maxY);
            Vector2D heading = Vector2D.randomVectorFixedMagnitude(1.0);

            BallEntity ballModel = new BallEntity(loc, heading, speed, radius, radius);
            balls.add(ballModel);
        }
    }




    /**
     * Get the number of balls the manger holds
     *
     * @return num balls
     */
    public int getNumBalls()
    {
        return balls.size();
    }




    /**
     * Get an iterable over the balls
     *
     * @return Iterable of balls
     */
    public Iterable<BallEntity> getBalls()
    {
        return balls;
    }




    /**
     * Increase elasticity between ball
     */
    public void increaseElasticity()
    {
        double elasticity = collisionEngine.getElasticity();

        elasticity += .05;

        collisionEngine.setElasticity(elasticity);
    }




    /**
     * Decrease elasticity between balls
     *
     */
    public void decreaseElasticity()
    {
        double elasticity = collisionEngine.getElasticity();

        elasticity -= .05;

        collisionEngine.setElasticity(elasticity);
    }




    /**
     * Get the current elasticity between balls
     *
     * @return elasticity
     */
    public double getElasticity()
    {
        return collisionEngine.getElasticity();
    }




    /**
     * Update all balls for delta time
     *
     * @param delta the time duration to advance the simulation
     */
    public void update(double delta)
    {
       
        //check for balls that will collide during this frame
        for (int i = 0; i < balls.size() - 1; i++)
        {
            for (int j = i + 1; j < balls.size(); j++)
            {

                BallEntity one = balls.get(i);
                BallEntity two = balls.get(j);
                if (collisionEngine.willCollide(one, two, delta))
                {
                    collisionPairs.add(new BallPair(one, two));
                }
            }
        }
        
        
        //update all of the balls
        for (BallEntity ball : balls)
        {
            ball.update(delta);
        }
        
        //handle all of the collisions that occured
        for(BallPair ballPair: collisionPairs)
        {
            collisionEngine.collisionResponse(ballPair.getOne(), ballPair.getTwo());
        }
        collisionPairs.clear();
        
        //handle collisions between all balls and the bounds of the world
        for (BallEntity ball : balls)
        {
            if (collisionEngine.areColliding(ball, world))
            {
                collisionEngine.collisionResponse(ball, world);
            }
        }
            
    }

    
    /**
     * This is a helper class for pair of balls used for collision 
     * detection and response. You could make this generic... but its
     * really only used in here. 
     */
    private class BallPair
    {
        private BallEntity one;
        private BallEntity two;

        private BallPair(BallEntity one, BallEntity two)
        {
            this.one = one;
            this.two = two;
        }

        private BallEntity getOne()
        {
            return one;
        }
        private BallEntity getTwo()
        {
            return two;
        }
    }


}


